package com.changgou.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.changgou.goods.feign.SkuFeign;
import com.changgou.goods.pojo.Sku;
import com.changgou.search.dao.SkuEsMapper;
import com.changgou.search.pojo.SkuInfo;
import com.changgou.search.service.SkuService;
import entity.Result;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class SkuServiceImpl implements SkuService {
    /**
     * 导入索引库
     */
    @Autowired
    private SkuFeign skuFeign;
    @Autowired
    private SkuEsMapper skuEsMapper;
    //可以实现索引库的增删改查（crud）【高级搜索】
    @Autowired
    private ElasticsearchTemplate esTemplate;
    @Override
    public void importData() {
//首先要调用feign，查询List<Sku>
        Result<List<Sku>> skuAll = skuFeign.findByStatus("0");  //商品状态，1-正常，2-下架，3-删除
        //将List<Sku>转成List<SkuInfo>
        //List<Sku>-->skuJSON-->List<SkuInfo>
        List<SkuInfo> skuInfoList = JSON.parseArray(JSON.toJSONString(skuAll.getData()), SkuInfo.class);
//获取spec--> Map(String) -->Map类型
        for(SkuInfo skuInfo: skuInfoList){
            Map<String,Object> spceMap = JSON.parseObject(skuInfo.getSpec(),Map.class);
            skuInfo.setSpecMap(spceMap);
        }
        //调用dao实现数据导入
        skuEsMapper.saveAll(skuInfoList);
    }


    /***
     * 关键字搜索
     *
     * @param searchMap
     */
    @Override
    public Map<String,Object> search(Map<String,String>searchMap){
        //条件封装
        NativeSearchQueryBuilder nativeSearchQueryBuilder = buildBasicQuery(searchMap);

        //搜索返回的数据结果集
        Map<String, Object> resultMap = searchList(nativeSearchQueryBuilder);

                        List<String> putAllSpec = ((List<SkuInfo>) resultMap.get("rows"))
                                .stream()
                                .map(SkuInfo::getSpec)
                                .collect(Collectors.toList());
                        resultMap.put("specList", putAllSpec(putAllSpec));

if(searchMap==null || StringUtils.isEmpty(searchMap.get("category"))){
    //分类分组查询实现；优化，当用户选择了分类，将分类作为搜索条件，则不需要对分类进行分组搜索，因为分组搜索的数据是用于显示分类搜索条件的
    Set<String> categoryList = ((List<SkuInfo>)resultMap.get("rows"))
            .stream()
            .map(SkuInfo::getCategoryName)
            .collect(Collectors.toSet());

    resultMap.put("categoryList",categoryList);
}

if(searchMap==null || StringUtils.isEmpty(searchMap.get("brand"))){
    //品牌查询；当用户选择了品牌，将品牌作为搜索条件，则不需要对分类进行品牌搜索
    Set<String> brandList = ((List<SkuInfo>)resultMap.get("rows"))
            .stream()
            .map(brand->brand.getBrandName())
            .collect(Collectors.toSet());
    resultMap.put("brandList",brandList);
}

        /*规格查询
        Map<String, Object> stringSetMap = searchGroupList(nativeSearchQueryBuilder);
        resultMap.putAll(stringSetMap);*/
        return resultMap;
    }

    /**
     * 删除Sku集合
     *
     * @param list
     * @return
     */
    @Override
    public void deleteList(List<Sku> list) {
        for (Sku sku : list) {
            skuEsMapper.deleteById(Long.parseLong(sku.getId()));
        }
    }

    /**
     * 修改Sku集合
     *
     * @param list
     * @return
     */
    @Override
    public void updateList(List<Sku> list) {
        List<SkuInfo> skuInfos = JSON.parseArray(JSON.toJSONString(list), SkuInfo.class);
        //将spec字符串转化成map，map的key会自动生成Field
        for (SkuInfo skuInfo : skuInfos) {
            Map<String, Object> map = JSON.parseObject(skuInfo.getSpec(), Map.class);
            skuInfo.setSpecMap(map);
        }
        skuEsMapper.saveAll(skuInfos);
    }

    /**
     * 分组查询:规格
     */
    public Map<String, Object> searchGroupList(NativeSearchQueryBuilder nativeSearchQueryBuilder){
        HashMap<String, Object> groupMapResult = new HashMap<>();
        //在条件构建基础上进行操作；spec.keyword不分词查询，因为要进行分组；size指定数据返回条数，适量而行
        //参数：别名，域
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuSpec").field("spec.keyword").size(10000));     //size()；默认是10条数据
        AggregatedPage<SkuInfo> aggregatedPage = esTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class);
       StringTerms stringTerms = aggregatedPage.getAggregations().get("skuSpec");
        List<String> groupList = getGroupList(stringTerms);
        //规格汇总合并
        Map<String, Set<String>> allSpec = putAllSpec(groupList);
        groupMapResult.put("specList",allSpec);
        return groupMapResult;
    }

    public List<String> getGroupList(StringTerms stringTerms){
        List<String> groupStringList = new ArrayList<>();
        for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {
            String keyAsString = bucket.getKeyAsString();
            groupStringList.add(keyAsString);
        }
        return groupStringList;
    }

    public Map<String, Set<String>> putAllSpec(List<String> stringList) {
        //合并后的Map对象：将每个Map对象合并成一个Map<String,Set<String>>
        Map<String, Set<String>> allSpec = new HashMap<>();
        for (String spec:stringList) {
Map<String,String> specMap = JSON.parseObject(spec, Map.class);
            for (Map.Entry<String,String> entry:specMap.entrySet()
                 ) {
String key = entry.getKey();
 String value = entry.getValue();
                Set<String> specSetValue = allSpec.get(key);
                if(specSetValue == null){
                    specSetValue = new HashSet<>();
                }
                specSetValue.add(value);
 allSpec.put(key,specSetValue);
            }
        }
        return allSpec;
    }

    public NativeSearchQueryBuilder buildBasicQuery(Map<String, String> searchMap) {
        //创建查询对象的构建对象
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
     /*   if(searchMap == null){  //不能加上，因为有分页
            return nativeSearchQueryBuilder;
        }*/
        //filter返回的文档必须满足filter字句的条件，但是不会向must一样，参与计算分值
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();      //条件过滤

            //获取关键字的值
            String keywords = searchMap.get("keywords");
            if(!StringUtils.isEmpty(keywords)){
                //设置查询条件;不加上bool（条件过滤）的话，会把name域中的其它数据一起返回
                //nativeSearchQueryBuilder.withQuery(QueryBuilders.queryStringQuery(keywords).field("name"));
                //queryStringQuery对所有字段分词查询
                //设置主关键字查询;name要分词brandName不分词
                boolQueryBuilder.must(QueryBuilders.queryStringQuery(keywords).field("name"));
            }
            //term 代表完全匹配；match在搜索前会进行分词拆分，然后再进行匹配结果
            if(!StringUtils.isEmpty(searchMap.get("category"))){
                boolQueryBuilder.filter(QueryBuilders.termQuery("categoryName.keyword",searchMap.get("category")));
            }
            if(!StringUtils.isEmpty(searchMap.get("brand"))){       //参数:域，品牌数据值
                boolQueryBuilder.filter(QueryBuilders.termQuery("brandName.keyword",searchMap.get("brand")));
            }
            //规格过滤实现;多个值要进行循环操作
            for (Map.Entry<String, String> entry : searchMap.entrySet()) {
                String key = entry.getKey();
                //startswith以什么开头
                if(key.startsWith("spec_")){
                    String value = searchMap.get(key).replace("\\","");  //+号处理
                    boolQueryBuilder.filter(QueryBuilders.termQuery("specMap."+key.substring(5)+".keyword",value));
                }
            }
            String price = searchMap.get("price");
            if(!StringUtils.isEmpty(price)){
                price = price.replace("元","").replace("以上","");
                String[] split = price.split("-");
                //判断为null的原因是怕出现空指针异常，因为为null后（split.length >0）执行会出错
                if(split!=null&&split.length >0){    //gt大于
                    boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gt(Integer.parseInt(split[0])));
if(split.length == 2){      //lte小于
    boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(Integer.parseInt(split[1])));
}
                }
            }
            //搜索排序实现(ASC,DESC)
            String sortField = searchMap.get("sortField");  //排序域
            String sortRule = searchMap.get("sortRule");    //排序的规则（升序或降序）
            if(!StringUtils.isEmpty(sortField) && !StringUtils.isEmpty(sortRule)){
                nativeSearchQueryBuilder.withSort(new FieldSortBuilder(sortField).order(SortOrder.valueOf(sortRule)));
            }

            //分页，用户如果不传分页参数，默认第一页
            Integer pageNum = cocerterPage(searchMap) ; //默认第一页
            Integer size = 16; //默认显示的数据条数
            nativeSearchQueryBuilder.withPageable(PageRequest.of(pageNum-1,size));
            nativeSearchQueryBuilder.withQuery(boolQueryBuilder);

        return nativeSearchQueryBuilder;
    }

    //接收前端传过来的分页参数
    public Integer cocerterPage(Map<String,String> searchMap){
if(searchMap != null){
    String pageNum = searchMap.get("pageNum");
    try {
        return Integer.parseInt(pageNum);
    } catch (NumberFormatException e) {
    }
}
return 1;
    }
//高亮搜索在搜索数据的时候做
    public Map<String, Object> searchList(NativeSearchQueryBuilder nativeSearchQueryBuilder) {
        //高亮域配置
        HighlightBuilder.Field field = new HighlightBuilder.Field("name").preTags("<em style=\"color:red;\">").postTags("</em>").fragmentSize(100);
        //前缀;后缀
        //碎片长度 关键词数据的长度（关键词前后加起来的总长度）
        //添加高亮
        nativeSearchQueryBuilder.withHighlightFields(field);

        //构建查询对象,执行查询条件封装
        //AggregatedPage<SkuInfo> skuPage = esTemplate.queryForPage(nativeSearchQueryBuilder.build(),SkuInfo.class);
        //SearchResultMapper执行搜索后，将数据结果集封装到该对象中
        AggregatedPage<SkuInfo> skuPage = esTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                //存储所有转换后得到的高亮数据对象
                List<T> list = new ArrayList<>();
                //执行查询，获取数据结果集得到【非高亮数据|高亮数据】
                for (SearchHit hit : searchResponse.getHits()) {
                    //分析结果集数据，获取非高亮数据
                   SkuInfo skuInfo = JSON.parseObject(hit.getSourceAsString(),SkuInfo.class);
                    //分析结果集数据，获取高亮数据->只有某个域的高亮数据
                 HighlightField highlightField = hit.getHighlightFields().get("name");
                 if(highlightField != null && highlightField.getFragments() != null){
//读取高亮数据
                     Text[] fragments = highlightField.getFragments();      //得到高亮字符
                     StringBuffer stringBuffer = new StringBuffer();
                     for(Text text : fragments){
                         stringBuffer.append(text.toString());
                     }
                     //非高亮数据中指定的域替换成高亮数据
                     skuInfo.setName(stringBuffer.toString());
                 }
                 list.add((T)skuInfo);
                }
                //将结果返回
                /**
                 * 1携带高亮的数据；2分页对象；3搜索记录的总条数
                 */
                return new AggregatedPageImpl<T>(list,pageable,searchResponse.getHits().getTotalHits());
            }
        });


//返回结果
        Map<String,Object> resultMap = new HashMap();
        resultMap.put("rows",skuPage.getContent());  //获取数据结果集
        resultMap.put("total",skuPage.getTotalElements());     //获取总记录数
        resultMap.put("totalPages",skuPage.getTotalPages());        //总页数;分页实现了就能自动解决了的

        //构建的搜索信息封装
        Pageable pageable = nativeSearchQueryBuilder.build().getPageable();
        int pageNumber = pageable.getPageNumber(); //当前页
        int size=pageable.getPageSize(); //每页显示的数据条数
        resultMap.put("pageNumber",pageNumber+1);
        resultMap.put("pageSize",size);
        return resultMap;
    }
}
